home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / innd / nc.c < prev    next >
C/C++ Source or Header  |  1993-03-18  |  24KB  |  1,059 lines

  1. /*  $Revision: 1.35 $
  2. **
  3. **  Routines for the NNTP channel.  Other channels get the descriptors which
  4. **  we turn into NNTP channels, and over which we speak NNTP.
  5. */
  6. #include "innd.h"
  7. #include "dbz.h"
  8.  
  9.  
  10. #define BAD_COMMAND_COUNT    10
  11. #define WIP_CHECK        (1 * 60)
  12. #define SAVE_AMT        10
  13. #define ART_EOF(c, s)        \
  14.     ((c) >= 5 && (s)[-5] == '\r' && (s)[-4] == '\n' && (s)[-3] == '.' \
  15.      && (s)[-2] == '\r' && (s)[-1] == '\n')
  16.  
  17.  
  18. /*
  19. **  An entry in the dispatch table.  The name, and implementing function,
  20. **  of every command we support.
  21. */
  22. typedef struct _NCDISPATCH {
  23.     STRING    Name;
  24.     FUNCPTR    Function;
  25.     int        Size;
  26. } NCDISPATCH;
  27.  
  28.  
  29. /*
  30. **  Information about the work in progress on all our open channels.
  31. */
  32. typedef struct _WIP {
  33.     char    *MessageID;
  34.     long    Size;
  35.     time_t    Timestamp;
  36.     BUFFER    Replic;
  37.     BOOL    Wanted;
  38. } WIP;
  39.  
  40.  
  41. #if    0
  42. static FUNCTYPE    NCarticle();
  43. #endif    /* 0 */
  44. static FUNCTYPE    NCauthinfo();
  45. static FUNCTYPE    NChead();
  46. static FUNCTYPE    NChelp();
  47. static FUNCTYPE    NCihave();
  48. static FUNCTYPE    NClist();
  49. static FUNCTYPE    NCmode();
  50. static FUNCTYPE    NCquit();
  51. static FUNCTYPE    NCstat();
  52. static FUNCTYPE    NCxpath();
  53. static FUNCTYPE    NCxreplic();
  54. static FUNCTYPE    NC_unimp();
  55.  
  56. STATIC int        NCcount;    /* Number of open connections    */
  57. STATIC int        NCwipsize;    /* Size of NCwip array        */
  58. STATIC char        *NCfreelist[5];    /* Free string list        */
  59. STATIC WIP        *NCwip;        /* Work-in-progress        */
  60. STATIC WIP        NCnullwip;
  61. STATIC NCDISPATCH    NCcommands[] = {
  62. #if    0
  63.     {    "article",    NCarticle },
  64. #else
  65.     {    "article",    NC_unimp },
  66. #endif    /* 0 */
  67.     {    "authinfo",    NCauthinfo },
  68.     {    "help",        NChelp    },
  69.     {    "ihave",    NCihave    },
  70.     {    "list",        NClist    },
  71.     {    "mode",        NCmode    },
  72.     {    "quit",        NCquit    },
  73.     {    "head",        NChead    },
  74.     {    "stat",        NCstat    },
  75.     {    "body",        NC_unimp },
  76.     {    "group",    NC_unimp },
  77.     {    "last",        NC_unimp },
  78.     {    "newgroups",    NC_unimp },
  79.     {    "newnews",    NC_unimp },
  80.     {    "next",        NC_unimp },
  81.     {    "post",        NC_unimp },
  82.     {    "slave",    NC_unimp },
  83.     {    "xhdr",        NC_unimp },
  84.     {    "xpath",    NCxpath    },
  85.     {    "xreplic",    NCxreplic }
  86. };
  87. STATIC char        *NCquietlist[] = {
  88.     INND_QUIET_BADLIST
  89. };
  90. STATIC char        NCterm[] = "\r\n";
  91. STATIC char        NCdotterm[] = ".\r\n";
  92. STATIC char        NCbadcommand[] = NNTP_BAD_COMMAND;
  93. STATIC STRING        NCgreeting;
  94.  
  95.  
  96. /*
  97. **  Clear the work-in-progress entry and wake up anyone who might
  98. **  have been waiting for us.
  99. */
  100. STATIC void
  101. NCclearwip(wp)
  102.     register WIP    *wp;
  103. {
  104.     char        *p;
  105.  
  106.     if ((p = wp->MessageID) != NULL)
  107.     *p = '\0';
  108.     wp->Size = 0;
  109.     if (wp->Wanted) {
  110.         wp->Wanted = FALSE;
  111.     SCHANwakeup((POINTER)wp);
  112.     }
  113. }
  114.  
  115.  
  116. /*
  117. **  Write an NNTP reply message.
  118. */
  119. STATIC void
  120. NCwritetext(cp, text)
  121.     CHANNEL    *cp;
  122.     char    *text;
  123. {
  124.     RCHANremove(cp);
  125.     WCHANset(cp, text, (int)strlen(text));
  126.     WCHANappend(cp, NCterm, STRLEN(NCterm));
  127.     WCHANadd(cp);
  128.     if (Tracing || cp->Tracing)
  129.     syslog(L_TRACE, "%s > %s", CHANname(cp), text);
  130. }
  131.  
  132.  
  133. /*
  134. **  Tell the NNTP channel to go away.
  135. */
  136. STATIC void
  137. NCwriteshutdown(cp, text)
  138.     CHANNEL    *cp;
  139.     char    *text;
  140. {
  141.     RCHANremove(cp);
  142.     WCHANset(cp, NNTP_GOODBYE, STRLEN(NNTP_GOODBYE));
  143.     WCHANappend(cp, " ", 1);
  144.     WCHANappend(cp, text, (int)strlen(text));
  145.     WCHANappend(cp, NCterm, STRLEN(NCterm));
  146.     WCHANadd(cp);
  147.     cp->State = CSwritegoodbye;
  148. }
  149.  
  150.  
  151. /*
  152. **  If a Message-ID is bad, write a reject message and return TRUE.
  153. */
  154. STATIC BOOL
  155. NCbadid(cp, p)
  156.     register CHANNEL    *cp;
  157.     register char    *p;
  158. {
  159.     if (ARTidok(p))
  160.     return FALSE;
  161.  
  162.     NCwritetext(cp, NNTP_HAVEIT_BADID);
  163.     syslog(L_NOTICE, "%s bad_messageid %s", CHANname(cp), MaxLength(p, p));
  164.     return TRUE;
  165. }
  166.  
  167.  
  168. /*
  169. **  We have an entire article collected; try to post it.  If we're
  170. **  not running, drop the article or just pause and reschedule.
  171. */
  172. STATIC void
  173. NCpostit(cp)
  174.     register CHANNEL    *cp;
  175. {
  176.     STRING        response;
  177.     WIP            *wp;
  178.  
  179.     /* Note that some use break, some use return here. */
  180.     switch (Mode) {
  181.     default:
  182.     syslog(L_ERROR, "%s internal NCpostit mode %d", CHANname(cp), Mode);
  183.     return;
  184.     case OMpaused:
  185.     SCHANadd(cp, (time_t)(Now.time + PAUSE_RETRY_TIME), (POINTER)&Mode,
  186.         NCpostit, (POINTER)NULL);
  187.     return;
  188.     case OMrunning:
  189.     wp = &NCwip[cp->fd];
  190.     response = ARTpost(cp, AmSlave ? &wp->Replic : NULL, wp->MessageID);
  191.     if (atoi(response) == NNTP_TOOKIT_VAL)
  192.         cp->Received++;
  193.     else
  194.         cp->Rejected++;
  195.     cp->Reported++;
  196.     if (cp->Reported >= NNTP_ACTIVITY_SYNC) {
  197.         syslog(L_NOTICE,
  198.         "%s checkpoint seconds %ld accepted %ld refused %ld rejected %ld",
  199.         CHANname(cp), (long)(Now.time - cp->Started),
  200.         cp->Received, cp->Refused, cp->Rejected);
  201.         cp->Reported = 0;
  202.     }
  203.     NCwritetext(cp, response);
  204.     cp->State = CSgetcmd;
  205.     break;
  206.  
  207.     case OMthrottled:
  208.     NCwriteshutdown(cp, ModeReason);
  209.     cp->Rejected++;
  210.     break;
  211.     }
  212.  
  213.     /* Clear the work-in-progress entry. */
  214.     NCclearwip(&NCwip[cp->fd]);
  215. }
  216.  
  217.  
  218. /*
  219. **  Write-done function.  Close down or set state for what we expect to
  220. **  read next.
  221. */
  222. STATIC FUNCTYPE
  223. NCwritedone(cp)
  224.     register CHANNEL    *cp;
  225. {
  226.     switch (cp->State) {
  227.     default:
  228.     syslog(L_ERROR, "%s internal NCwritedone state %d",
  229.         CHANname(cp), cp->State);
  230.     break;
  231.  
  232.     case CSwritegoodbye:
  233.     if (NCcount > 0)
  234.         NCcount--;
  235.     CHANclose(cp, CHANname(cp));
  236.     break;
  237.  
  238.     case CSgetcmd:
  239.     case CSgetauth:
  240.     case CSgetarticle:
  241.     case CSgetrep:
  242.     RCHANadd(cp);
  243.     break;
  244.     }
  245. }
  246.  
  247.  
  248.  
  249. #if    0
  250. /*
  251. **  The "article" command.
  252. */
  253. STATIC FUNCTYPE
  254. NCarticle(cp)
  255.     register CHANNEL    *cp;
  256. {
  257.     register char    *p;
  258.     register char    *q;
  259.     char        *art;
  260.  
  261.     /* Snip off the Message-ID. */
  262.     for (p = cp->In.Data + STRLEN("article"); ISWHITE(*p); p++)
  263.     continue;
  264.     if (NCbadid(cp, p))
  265.     return;
  266.  
  267.     /* Get the article filenames, and the article header+body. */
  268.     if ((art = ARTreadarticle(HISfilesfor(p))) == NULL) {
  269.     NCwritetext(cp, NNTP_DONTHAVEIT);
  270.     return;
  271.     }
  272.  
  273.     /* Write it. */
  274.     NCwritetext(cp, NNTP_ARTICLE_FOLLOWS);
  275.     for (p = art; ((q = strchr(p, '\n')) != NULL); p = q + 1) {
  276.     if (*p == '.')
  277.         WCHANappend(cp, ".", 1);
  278.     WCHANappend(cp, p, q - p);
  279.     WCHANappend(cp, NCterm, STRLEN(NCterm));
  280.     }
  281.  
  282.     /* Write the terminator. */
  283.     WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
  284. }
  285. #endif    /* 0 */
  286.  
  287.  
  288. /*
  289. **  The "head" command.
  290. */
  291. STATIC FUNCTYPE
  292. NChead(cp)
  293.     CHANNEL        *cp;
  294. {
  295.     register char    *p;
  296.     register char    *q;
  297.     char        *head;
  298.  
  299.     /* Snip off the Message-ID. */
  300.     for (p = cp->In.Data + STRLEN("head"); ISWHITE(*p); p++)
  301.     continue;
  302.     if (NCbadid(cp, p))
  303.     return;
  304.  
  305.     /* Get the article filenames, and the header. */
  306.     if ((head = ARTreadheader(HISfilesfor(p))) == NULL) {
  307.     NCwritetext(cp, NNTP_DONTHAVEIT);
  308.     return;
  309.     }
  310.  
  311.     /* Write it. */
  312.     NCwritetext(cp, NNTP_HEAD_FOLLOWS);
  313.     for (p = head; ((q = strchr(p, '\n')) != NULL); p = q + 1) {
  314.     if (*p == '.')
  315.         WCHANappend(cp, ".", 1);
  316.     WCHANappend(cp, p, q - p);
  317.     WCHANappend(cp, NCterm, STRLEN(NCterm));
  318.     }
  319.  
  320.     /* Write the terminator. */
  321.     WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
  322. }
  323.  
  324.  
  325. /*
  326. **  The "stat" command.
  327. */
  328. STATIC FUNCTYPE
  329. NCstat(cp)
  330.     CHANNEL        *cp;
  331. {
  332.     register char    *p;
  333.     char        buff[SMBUF];
  334.  
  335.     /* Snip off the Message-ID. */
  336.     for (p = cp->In.Data + STRLEN("stat"); ISWHITE(*p); p++)
  337.     continue;
  338.     if (NCbadid(cp, p))
  339.     return;
  340.  
  341.     /* Get the article filenames; read the header (to make sure not
  342.      * the article is still here). */
  343.     if (ARTreadheader(HISfilesfor(p)) == NULL) {
  344.     NCwritetext(cp, NNTP_DONTHAVEIT);
  345.     return;
  346.     }
  347.  
  348.     /* Write the message. */
  349.     (void)sprintf(buff, "%d 0 %s", NNTP_NOTHING_FOLLOWS_VAL, p);
  350.     NCwritetext(cp, buff);
  351. }
  352.  
  353.  
  354. /*
  355. **  The "authinfo" command.  Actually, we come in here whenever the
  356. **  channel is in CSgetauth state and we just got a command.
  357. */
  358. STATIC FUNCTYPE
  359. NCauthinfo(cp)
  360.     register CHANNEL    *cp;
  361. {
  362.     static char        AUTHINFO[] = "authinfo ";
  363.     static char        PASS[] = "pass ";
  364.     static char        USER[] = "user ";
  365.     register char    *p;
  366.  
  367.     p = cp->In.Data;
  368.  
  369.     /* Allow the poor sucker to quit. */
  370.     if (caseEQ(p, "quit")) {
  371.     NCquit(cp);
  372.     return;
  373.     }
  374.  
  375.     /* Otherwise, make sure we're only getting "authinfo" commands. */
  376.     if (!caseEQn(p, AUTHINFO, STRLEN(AUTHINFO))) {
  377.     NCwritetext(cp, NNTP_AUTH_NEEDED);
  378.     return;
  379.     }
  380.     for (p += STRLEN(AUTHINFO); ISWHITE(*p); p++)
  381.     continue;
  382.  
  383.     /* Ignore "authinfo user" commands, since we only care about the
  384.      * password. */
  385.     if (caseEQn(p, USER, STRLEN(USER))) {
  386.     NCwritetext(cp, NNTP_AUTH_NEXT);
  387.     return;
  388.     }
  389.  
  390.     /* Now make sure we're getting only "authinfo pass" commands. */
  391.     if (!caseEQn(p, PASS, STRLEN(PASS))) {
  392.     NCwritetext(cp, NNTP_AUTH_NEEDED);
  393.     return;
  394.     }
  395.     for (p += STRLEN(PASS); ISWHITE(*p); p++)
  396.     continue;
  397.  
  398.     /* Got the password -- is it okay? */
  399.     if (!RCauthorized(cp, p)) {
  400.     NCwritetext(cp, NNTP_AUTH_BAD);
  401.     cp->State = CSwritegoodbye;
  402.     }
  403.     else {
  404.     NCwritetext(cp, NNTP_AUTH_OK);
  405.     cp->State = CSgetcmd;
  406.     }
  407. }
  408.  
  409.  
  410. /*
  411. **  Is someone already sending us this article?
  412. */
  413. STATIC BOOL
  414. NCinprogress(cp, id, who)
  415.     CHANNEL        *cp;
  416.     register char    *id;
  417.     WIP            **who;
  418. {
  419.     register WIP    *wp;
  420.     register char    *p;
  421.     register int    i;
  422.  
  423.     for (i = NCwipsize, wp = NCwip; --i >= 0; wp++)
  424.     if ((p = wp->MessageID) != NULL && *p == *id && EQ(p, id)
  425.      && Now.time - wp->Timestamp < WIP_CHECK) {
  426.         *who = wp;
  427.         return TRUE;
  428.     }
  429.     wp = &NCwip[cp->fd];
  430.     if (wp->MessageID == NULL) {
  431.     for (i = SIZEOF(NCfreelist); --i >= 0; )
  432.         if (NCfreelist[i] != NULL) {
  433.         wp->MessageID = NCfreelist[i];
  434.         NCfreelist[i] = NULL;
  435.         break;
  436.         }
  437.     if (i < 0)
  438.         wp->MessageID = NEW(char, DBZMAXKEY + 3);
  439.     }
  440.     (void)strcpy(wp->MessageID, id);
  441.     return FALSE;
  442. }
  443.  
  444.  
  445. /*
  446. **  The "help" command.
  447. */
  448. STATIC FUNCTYPE
  449. NChelp(cp)
  450.     register CHANNEL    *cp;
  451. {
  452.     static char        LINE1[] = "For more information, contact \"";
  453.     static char        LINE2[] = "\" at this machine.";
  454.     register NCDISPATCH    *dp;
  455.  
  456.     NCwritetext(cp, NNTP_HELP_FOLLOWS);
  457.     for (dp = NCcommands; dp < ENDOF(NCcommands); dp++)
  458.     if (dp->Function != NC_unimp) {
  459.         WCHANappend(cp, "\t", 1);
  460.         WCHANappend(cp, dp->Name, dp->Size);
  461.         WCHANappend(cp, NCterm, STRLEN(NCterm));
  462.     }
  463.     WCHANappend(cp, LINE1, STRLEN(LINE1));
  464.     WCHANappend(cp, NEWSMASTER, STRLEN(NEWSMASTER));
  465.     WCHANappend(cp, LINE2, STRLEN(LINE2));
  466.     WCHANappend(cp, NCterm, STRLEN(NCterm));
  467.     WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
  468. }
  469.  
  470.  
  471. #if    !defined(NNTP_RESENDIT_LATER)
  472. /*
  473. **  We woke up because we got offered an article that was already in
  474. **  progress somewhere else.  If the other channel finished, then we
  475. **  don't want the article, otherwise let's accept it.
  476. */
  477. STATIC FUNCTYPE
  478. NCwaitfor(cp)
  479.     register CHANNEL    *cp;
  480. {
  481.     WIP            *who;
  482.  
  483.     if (HIShavearticle(cp->Argument)) {
  484.     NCwritetext(cp, NNTP_HAVEIT);
  485.     DISPOSE(cp->Argument);
  486.     cp->Argument = NULL;
  487.     }
  488.     else if (NCinprogress(cp, cp->Argument, &who)) {
  489.     who->Wanted = TRUE;
  490.     SCHANadd(cp, (time_t)(Now.time + WIP_CHECK / 2 + 1), (POINTER)who,
  491.         NCwaitfor, (POINTER)cp->Argument);
  492.     }
  493.     else {
  494.     NCwritetext(cp, NNTP_SENDIT);
  495.     cp->State = CSgetarticle;
  496.     DISPOSE(cp->Argument);
  497.     cp->Argument = NULL;
  498.     }
  499. }
  500. #endif    /* !defined(NNTP_RESENDIT_LATER) */
  501.  
  502.  
  503. /*
  504. **  The "ihave" command.  Check the Message-ID, and see if we want the
  505. **  article or not.  Set the state appropriately.
  506. */
  507. STATIC FUNCTYPE
  508. NCihave(cp)
  509.     CHANNEL        *cp;
  510. {
  511.     register char    *p;
  512.     WIP            *who;
  513.  
  514.     if (AmSlave) {
  515.     NCwritetext(cp, NCbadcommand);
  516.     return;
  517.     }
  518.  
  519.     /* Snip off the Message-ID. */
  520.     for (p = cp->In.Data + STRLEN("ihave"); ISWHITE(*p); p++)
  521.     continue;
  522.     if (NCbadid(cp, p))
  523.     return;
  524.  
  525.     if (HIShavearticle(p)) {
  526.     cp->Refused++;
  527.     NCwritetext(cp, NNTP_HAVEIT);
  528.     }
  529.     else if (NCinprogress(cp, p, &who)) {
  530. #if    defined(NNTP_RESENDIT_LATER)
  531.     NCwritetext(cp, NNTP_RESENDIT_LATER);
  532. #else
  533.     /* Somebody else is sending it to us; wait until they're done. */
  534.     who->Wanted = TRUE;
  535.     SCHANadd(cp, (time_t)(Now.time + WIP_CHECK + 1), (POINTER)who,
  536.         NCwaitfor, (POINTER)COPY(p));
  537.     /* Clear input buffer. */
  538.     cp->In.Used = 0;
  539. #endif    /* defined(NNTP_RESENDIT_LATER) */
  540.     }
  541.     else {
  542.     NCwritetext(cp, NNTP_SENDIT);
  543.     cp->State = CSgetarticle;
  544.     }
  545. }
  546.  
  547.  
  548. /*
  549. **  The "list" command.  Send the active file.
  550. */
  551. STATIC FUNCTYPE
  552. NClist(cp)
  553.     register CHANNEL    *cp;
  554. {
  555.     register char    *p;
  556.     register char    *q;
  557.     char        *trash;
  558.     char        *end;
  559.  
  560.     for (p = cp->In.Data + STRLEN("list"); ISWHITE(*p); p++)
  561.     continue;
  562.     if (caseEQ(p, "newsgroups")) {
  563.     trash = p = ReadInFile(_PATH_NEWSGROUPS, (struct stat *)NULL);
  564.     end = p + strlen(p);
  565.     }
  566.     else if (caseEQ(p, "active.times")) {
  567.     trash = p = ReadInFile(_PATH_ACTIVETIMES, (struct stat *)NULL);
  568.     end = p + strlen(p);
  569.     }
  570.     else if (*p == '\0' || (caseEQ(p, "active"))) {
  571.     p = ICDreadactive(&end);
  572.     trash = NULL;
  573.     }
  574.     else {
  575.     NCwritetext(cp, NCbadcommand);
  576.     return;
  577.     }
  578.  
  579.     /* Loop over all lines, sending the text and \r\n. */
  580.     NCwritetext(cp, NNTP_LIST_FOLLOWS);
  581.     for (; p < end && (q = strchr(p, '\n')) != NULL; p = q + 1) {
  582.     WCHANappend(cp, p, q - p);
  583.     WCHANappend(cp, NCterm, STRLEN(NCterm));
  584.     }
  585.     WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
  586.     if (trash)
  587.     DISPOSE(trash);
  588. }
  589.  
  590.  
  591. /*
  592. **  The "mode" command.  Hand off the channel.
  593. */
  594. STATIC FUNCTYPE
  595. NCmode(cp)
  596.     register CHANNEL    *cp;
  597. {
  598.     register char    *p;
  599.     HANDOFF        h;
  600.  
  601.     /* Skip the first word, get the argument. */
  602.     for (p = cp->In.Data + STRLEN("mode"); ISWHITE(*p); p++)
  603.     continue;
  604.  
  605.     if (caseEQ(p, "reader"))
  606.     h = HOnnrpd;
  607.     else if (caseEQ(p, "query"))
  608.     h = HOnnrqd;
  609.     else {
  610.     NCwritetext(cp, NCbadcommand);
  611.     return;
  612.     }
  613.     RChandoff(cp->fd, h);
  614.     if (NCcount > 0)
  615.     NCcount--;
  616.     CHANclose(cp, CHANname(cp));
  617. }
  618.  
  619.  
  620. /*
  621. **  The "quit" command.  Acknowledge, and set the state to closing down.
  622. */
  623. STATIC FUNCTYPE
  624. NCquit(cp)
  625.     CHANNEL        *cp;
  626. {
  627.     register WIP    *wp;
  628.     register int    i;
  629.  
  630.     wp = &NCwip[cp->fd];
  631.     for (i = SIZEOF(NCfreelist); --i >= 0; )
  632.     if (NCfreelist[i] == NULL) {
  633.         NCfreelist[i] = wp->MessageID;
  634.         wp->MessageID = NULL;
  635.         break;
  636.     }
  637. #if    0
  638.     if (i < 0) {
  639.     DISPOSE(wp->MessageID);
  640.     wp->MessageID = NULL;
  641.     }
  642. #endif    /* 0 */
  643.     NCwritetext(cp, NNTP_GOODBYE_ACK);
  644.     cp->State = CSwritegoodbye;
  645. }
  646.  
  647.  
  648. /*
  649. **  The "xpath" command.  Return the paths for an article is.
  650. */
  651. STATIC FUNCTYPE
  652. NCxpath(cp)
  653.     CHANNEL        *cp;
  654. {
  655.     static BUFFER    Reply;
  656.     register char    *p;
  657.     register int    i;
  658.  
  659.     /* Nip off the Message-ID. */
  660.     for (p = cp->In.Data + STRLEN("xpath"); ISWHITE(*p); p++)
  661.     continue;
  662.     if (NCbadid(cp, p))
  663.     return;
  664.  
  665.     if ((p = HISfilesfor(p)) == NULL) {
  666.     NCwritetext(cp, NNTP_DONTHAVEIT);
  667.     return;
  668.     }
  669.     i = 3 + 1 + strlen(p);
  670.     if (Reply.Data == NULL) {
  671.     Reply.Size = i;
  672.     Reply.Data = NEW(char, i + 1);
  673.     }
  674.     else if (Reply.Size < i) {
  675.     Reply.Size = i;
  676.     RENEW(Reply.Data, char, i + 1);
  677.     }
  678.     (void)sprintf(Reply.Data, "%d %s", NNTP_NOTHING_FOLLOWS_VAL, p);
  679.     NCwritetext(cp, Reply.Data);
  680. }
  681.  
  682.  
  683. /*
  684. **  The "xreplic" command.  Take an article and the places to file it.
  685. */
  686. STATIC FUNCTYPE
  687. NCxreplic(cp)
  688.     CHANNEL        *cp;
  689. {
  690.     register char    *p;
  691.     register BUFFER    *bp;
  692.     register int    i;
  693.  
  694.     if (!RCismaster(cp->Address)) {
  695.     NCwritetext(cp, NCbadcommand);
  696.     return;
  697.     }
  698.  
  699.     /* Stash the filename arguments. */
  700.     for (p = cp->In.Data + STRLEN("xreplic"); ISWHITE(*p); p++)
  701.     continue;
  702.     i = cp->In.Used - (p - cp->In.Data) + 1;
  703.     bp = &NCwip[cp->fd].Replic;
  704.     if (bp->Data == NULL) {
  705.     bp->Size = i;
  706.     bp->Data = NEW(char, i);
  707.     }
  708.     BUFFset(bp, p, i);
  709.     bp->Used = bp->Left;
  710.  
  711.     /* Tell master to send it to us. */
  712.     NCwritetext(cp, NNTP_SENDIT);
  713.     cp->State = CSgetrep;
  714. }
  715.  
  716.  
  717. /*
  718. **  The catch-all for inimplemented commands.
  719. */
  720. STATIC FUNCTYPE
  721. NC_unimp(cp)
  722.     CHANNEL        *cp;
  723. {
  724.     register char    *p;
  725.     char        buff[SMBUF];
  726.  
  727.     /* Nip off the first word. */
  728.     for (p = cp->In.Data; *p && !ISWHITE(*p); p++)
  729.     continue;
  730.     *p = '\0';
  731.     (void)sprintf(buff, "%d \"%s\" not implemented; try \"help\".",
  732.         NNTP_BAD_COMMAND_VAL, MaxLength(cp->In.Data, cp->In.Data));
  733.     NCwritetext(cp, buff);
  734. }
  735.  
  736.  
  737.  
  738. /*
  739. **  Remove the \r\n and leading dot escape that the NNTP protocol adds.
  740. */
  741. STATIC void
  742. NCclean(bp)
  743.     register BUFFER    *bp;
  744. {
  745.     register char    *end;
  746.     register char    *p;
  747.     register char    *dest;
  748.  
  749.     for (p = bp->Data, dest = p, end = p + bp->Used; p < end; ) {
  750.     if (p[0] == '\r' && p[1] == '\n') {
  751.         p += 2;
  752.         *dest++ = '\n';
  753.         if (p[0] == '.' && p[1] == '.') {
  754.         p += 2;
  755.         *dest++ = '.';
  756.         }
  757.     }
  758.     else
  759.         *dest++ = *p++;
  760.     }
  761.     *dest = '\0';
  762.     bp->Used = dest - bp->Data;
  763. }
  764.  
  765.  
  766. /*
  767. **  Read whatever data is available on the channel.  If we got the
  768. **  full amount (i.e., the command or the whole article) process it.
  769. */
  770. STATIC FUNCTYPE
  771. NCreader(cp)
  772.     register CHANNEL    *cp;
  773. {
  774.     register char    *p;
  775.     register NCDISPATCH    *dp;
  776.     register BUFFER    *bp;
  777.     register WIP    *wp;
  778.     STRING        q;
  779.     char        buff[SMBUF];
  780.     char        *av[2];
  781.     int            i;
  782.  
  783.     /* Read any data that's there; ignore errors (retry next time it's our
  784.      * turn) and if we got nothing, then it's EOF so mark it closed. */
  785.     if ((i = CHANreadtext(cp)) < 0) {
  786.     if (cp->BadReads++ >= BAD_IO_COUNT) {
  787.         if (NCcount > 0)
  788.         NCcount--;
  789.         CHANclose(cp, CHANname(cp));
  790.     }
  791.     return;
  792.     }
  793.     if (i == 0) {
  794.     NCcount--;
  795.     return;
  796.     }
  797.  
  798.     /* Update timestamp. */
  799.     wp = &NCwip[cp->fd];
  800.     wp->Timestamp = Now.time;
  801.  
  802.     bp = &cp->In;
  803.     p = &bp->Data[bp->Used];
  804.     switch (cp->State) {
  805.     default:
  806.     syslog(L_ERROR, "%s internal NCreader state %d",
  807.         CHANname(cp), cp->State);
  808.     break;
  809.  
  810.     case CSgetcmd:
  811.     case CSgetauth:
  812.     /* Did we get the whole command, terminated with "\r\n"? */
  813.     if (bp->Used < 2 || p[-2] != '\r' || p[-1] != '\n')
  814.         break;
  815.     p[-2] = '\0';
  816.     bp->Used -= 2;
  817.  
  818.     /* Ignore blank lines. */
  819.     if (bp->Used == 0)
  820.         break;
  821.     if (Tracing || cp->Tracing)
  822.         syslog(L_TRACE, "%s < %s", CHANname(cp), bp->Data);
  823.  
  824.     /* We got something -- stop sleeping (in case we were). */
  825.     SCHANremove(cp);
  826.     if (cp->Argument != NULL) {
  827.         DISPOSE(cp->Argument);
  828.         cp->Argument = NULL;
  829.     }
  830.  
  831.     if (cp->State == CSgetauth) {
  832.         if (caseEQn(bp->Data, "mode", 4))
  833.         NCmode(cp);
  834.         else
  835.         NCauthinfo(cp);
  836.         break;
  837.     }
  838.  
  839.     /* Loop through the command table. */
  840.     for (p = bp->Data, dp = NCcommands; dp < ENDOF(NCcommands); dp++)
  841.         if (caseEQn(p, dp->Name, dp->Size)) {
  842.         (*dp->Function)(cp);
  843.         cp->BadCommands = 0;
  844.         break;
  845.         }
  846.     if (dp == ENDOF(NCcommands)) {
  847.         NCwritetext(cp, NCbadcommand);
  848.         if (++(cp->BadCommands) >= BAD_COMMAND_COUNT)
  849.         cp->State = CSwritegoodbye;
  850.         for (i = 0; (p = NCquietlist[i]) != NULL; i++)
  851.         if (caseEQ(p, dp->Name))
  852.             break;
  853.         if (p == NULL)
  854.         syslog(L_NOTICE, "%s bad_command %s",
  855.             CHANname(cp), MaxLength(bp->Data, bp->Data));
  856.     }
  857.     break;
  858.  
  859.     case CSgetarticle:
  860.     case CSgetrep:
  861.     /* Reading an article; look for "\r\n.\r\n" terminator. */
  862.     if (!ART_EOF(bp->Used, p)) {
  863.         /* Check for the null article. */
  864.         if (bp->Used == 3
  865.          && p[-3] == '.' && p[-2] == '\r' && p[-1] == '\n') {
  866.         cp->Rejected++;
  867.         NCwritetext(cp, NNTP_REJECTIT_EMPTY);
  868.         cp->State = CSgetcmd;
  869.         bp->Used = 0;
  870.  
  871.         /* Clear the work-in-progress entry. */
  872.         NCclearwip(wp);
  873.         }
  874.  
  875.         /* Check for big articles. */
  876.         if (LargestArticle > SAVE_AMT && bp->Used > LargestArticle) {
  877.         /* Make some room, saving only the last few bytes. */
  878.         for (p = bp->Data, i = 0; i < SAVE_AMT; p++, i++)
  879.             p[0] = p[bp->Used - SAVE_AMT];
  880.         wp->Size += bp->Used - SAVE_AMT;
  881.         bp->Used = SAVE_AMT;
  882.         cp->State = CSeatarticle;
  883.         }
  884.         break;
  885.     }
  886.  
  887.     /* Strip article terminator and post the article. */
  888.     p[-3] = '\0';
  889.     bp->Used -= 3;
  890.     SCHANremove(cp);
  891.     if (cp->Argument != NULL) {
  892.         DISPOSE(cp->Argument);
  893.         cp->Argument = NULL;
  894.     }
  895.     NCclean(bp);
  896.     NCpostit(cp);
  897.     break;
  898.  
  899.     case CSeatarticle:
  900.     /* Eat the article and then complain that it was too large */
  901.     if (ART_EOF(bp->Used, p)) {
  902.         /* Reached the end of the article. */
  903.         SCHANremove(cp);
  904.         if (cp->Argument != NULL) {
  905.         DISPOSE(cp->Argument);
  906.         cp->Argument = NULL;
  907.         }
  908.         p = wp->MessageID;
  909.         i = wp->Size + bp->Used;
  910.         syslog(L_ERROR, "%s internal rejecting huge article %s (%d > %d)",
  911.         CHANname(cp), p ? p : "(null)", i, LargestArticle);
  912.         (void)sprintf(buff, "%d Article exceeds local limit of %ld bytes",
  913.             NNTP_REJECTIT_VAL, LargestArticle);
  914.         NCwritetext(cp, buff);
  915.         cp->State = CSgetcmd;
  916.         cp->Rejected++;
  917.  
  918.         /* Write a local cancel entry so nobody else gives it to us. */
  919.         if (p) {
  920.         av[0] = p;
  921.         av[1] = NULL;
  922.         if ((q = CCcancel(av)) != NULL)
  923.             syslog(L_ERROR, "%s cant cancel %s %s", LogName, av[0], q);
  924.         }
  925.  
  926.         /* Clear the work-in-progress entry. */
  927.         NCclearwip(wp);
  928.  
  929.         /* Reset input buffer to the default size; don't let realloc
  930.          * be lazy. */
  931.         DISPOSE(bp->Data);
  932.         bp->Size = START_BUFF_SIZE;
  933.         bp->Used = 0;
  934.         bp->Data = NEW(char, bp->Size);
  935.     }
  936.     else if (bp->Used > 8 * 1024) {
  937.         /* Make some room; save the last few bytes of the article */
  938.         for (p = bp->Data, i = 0; i < SAVE_AMT; p++, i++)
  939.         p[0] = p[bp->Used - SAVE_AMT + 0];
  940.         wp->Size += bp->Used - SAVE_AMT;
  941.         bp->Used = SAVE_AMT;
  942.     }
  943.     break;
  944.     }
  945. }
  946.  
  947.  
  948. /*
  949. **  Set up the NNTP channel state.
  950. */
  951. void
  952. NCsetup(i)
  953.     register int    i;
  954. {
  955.     register WIP    *wp;
  956.     register NCDISPATCH    *dp;
  957.     char        *p;
  958.     char        buff[SMBUF];
  959.  
  960.     /* Set the greeting message. */
  961.     if ((p = GetConfigValue(_CONF_PATHHOST)) == NULL)
  962.     /* Worked in main, now it fails?  Curious. */
  963.     p = Path.Data;
  964.     (void)sprintf(buff, "%d %s InterNetNews server %s ready",
  965.         NNTP_POSTOK_VAL, p, Version);
  966.     NCgreeting = COPY(buff);
  967.  
  968.     /* Set up the work-in-progress structure. */
  969.     for (wp = NCwip = NEW(WIP, i), NCwipsize = i; --i >= 0; wp++)
  970.     *wp = NCnullwip;
  971.  
  972.     /* Get the length of every command. */
  973.     for (dp = NCcommands; dp < ENDOF(NCcommands); dp++)
  974.     dp->Size = strlen(dp->Name);
  975. }
  976.  
  977.  
  978. /*
  979. **  Tear down our state.
  980. */
  981. void
  982. NCclose()
  983. {
  984.     register WIP    *wp;
  985.     register int    i;
  986.     register CHANNEL    *cp;
  987.     int            j;
  988.  
  989.     /* Close all incoming channels. */
  990.     for (j = 0; (cp = CHANiter(&j, CTnntp)) != NULL; ) {
  991.     if (NCcount > 0)
  992.         NCcount--;
  993.     CHANclose(cp, CHANname(cp));
  994.     }
  995.  
  996.     /* Free the WIP list. */
  997.     for (wp = NCwip, i = NCwipsize; --i >= 0; wp++) {
  998.     if (wp->MessageID)
  999.         DISPOSE(wp->MessageID);
  1000.     if (wp->Replic.Data)
  1001.         DISPOSE(wp->Replic.Data);
  1002.     }
  1003.     DISPOSE(NCwip);
  1004.     for (i = SIZEOF(NCfreelist); --i >= 0; )
  1005.     if (NCfreelist[i] != NULL)
  1006.         DISPOSE(NCfreelist[i]);
  1007. }
  1008.  
  1009.  
  1010. /*
  1011. **  Create an NNTP channel and print the greeting message.
  1012. */
  1013. CHANNEL *
  1014. NCcreate(fd, MustAuthorize)
  1015.     int            fd;
  1016.     BOOL        MustAuthorize;
  1017. {
  1018.     register CHANNEL    *cp;
  1019.     int            i;
  1020.  
  1021.     /* Create the channel. */
  1022.     cp = CHANcreate(fd, CTnntp, MustAuthorize ? CSgetauth : CSgetcmd,
  1023.         NCreader, NCwritedone);
  1024.     NCclearwip(&NCwip[cp->fd]);
  1025. #if    defined(SOL_SOCKET) && defined(SO_SNDBUF) && defined(SO_RCVBUF)
  1026.     i = 24 * 1024;
  1027.     if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&i, sizeof i) < 0)
  1028.     syslog(L_ERROR, "%s cant setsockopt(SNDBUF) %m", CHANname(cp));
  1029.     if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&i, sizeof i) < 0)
  1030.     syslog(L_ERROR, "%s cant setsockopt(RCVBUF) %m", CHANname(cp));
  1031. #endif    /* defined(SOL_SOCKET) && defined(SO_SNDBUF) && defined(SO_RCVBUF) */
  1032.  
  1033.     /* Now check our operating mode. */
  1034.     NCcount++;
  1035.     if (Mode == OMthrottled) {
  1036.     NCwriteshutdown(cp, ModeReason);
  1037.     return cp;
  1038.     }
  1039.     if (RejectReason) {
  1040.     NCwriteshutdown(cp, RejectReason);
  1041.     return cp;
  1042.     }
  1043.  
  1044.     /* See if we have too many channels. */
  1045.     if (MaxIncoming && NCcount >= MaxIncoming && !RCnolimit(cp)) {
  1046.     /* Recount, just in case we got out of sync. */
  1047.     for (NCcount = 0, i = 0; CHANiter(&i, CTnntp) != NULL; )
  1048.         NCcount++;
  1049.     if (NCcount >= MaxIncoming) {
  1050.         NCwriteshutdown(cp, "Too many connections");
  1051.         return cp;
  1052.     }
  1053.     }
  1054.     cp->BadReads = 0;
  1055.     cp->BadCommands = 0;
  1056.     NCwritetext(cp, NCgreeting);
  1057.     return cp;
  1058. }
  1059.